import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.HashMap;


public class Evaluation {

	//(<non variable> <valeur>)
	//environement lexicale
	private HashMap<String, Object> env;
	
	
	//intialise l'environement
	public void initEnv(HashMap<String, Object> penv) {
		env = penv;
	}
	
	private static Console console;
	
	
	public Evaluation(){}
	
	Evaluation(Console c){
		console = c;
	}
	

	//cr�e un environement pour l'execution des fct definie avec "pour"
	private HashMap<String, Object> makeEnv(ArrayList<Object> arg, ArrayList<Object> val, ArrayList<Object> argOp) {
		HashMap<String, Object> env = new HashMap<String, Object>();
	
		if (arg.size() == val.size()) {

                        for(int i=0 ;i<arg.size();i++)
                                env.put((String)arg.get(i), val.get(i));
                        
                       	 for(int i=0 ;i<argOp.size()/2;i++)
                       		env.put((String)argOp.get(i*2), argOp.get(i*2+1));
                        return env;
                }
                else
                       if(arg.size() <= val.size()) {
                    	   	int i=0;
                    		
                    		while(i<arg.size()) {
                    			  env.put((String)arg.get(i), val.get(i));
                    			  i++;	  
                    		}
                    		 for(int k=0 ;k<argOp.size();k+=2)
                       			env.put((String)argOp.get(k), argOp.get(k+1));
                    		
                    		while(i<val.size()) {
                    			env.put((String)argOp.get(i*2-arg.size()*2), val.get(i));
                    			i++;
                    		}
                    		return env;
                       }
                       else return null;
		} 


	
    //execute les fct definie avec "pour"
	private Object run(ArrayList<Object> list,  HashMap<String, Object> penv) throws ParseException, InvocationTargetException{
		
        Evaluation parser = new Evaluation();
        parser.initEnv(penv);
        
		return parser.evalCorps(list);
	}
	
	
	
	
	
	
	public Object evalCorps( ArrayList<Object> listeInstruction) throws InvocationTargetException {
		Object ret = null;
		ArrayList<Object> instruction;
		
		for (int i = 0; i < listeInstruction.size(); i++) {
			instruction = (ArrayList)listeInstruction.get(i);
	
			ret = eval(instruction);
		}
		return ret;		
	}
	
	public Object evalListe(ArrayList<Object> listeInstruction) throws InvocationTargetException{
		
		ArrayList<Object> instruction;
		ArrayList<Object> ret = new ArrayList<Object>();
		
		for (int i = 0; i < listeInstruction.size(); i++) {
			instruction = (ArrayList)listeInstruction.get(i);
			ret.add(eval(instruction));
		}
		return ret;		
	}
	
	
	public Object eval( ArrayList<Object> instruction) throws InvocationTargetException {
		
			switch ((Integer)instruction.get(0)) {
				case 0 : return instruction.get(1);
				
				
				//case 1 : return instruction.get(1);
				
				
				case 2 : ArrayList<Object> v = new ArrayList<Object>();
				
						 for(int i=2;i<instruction.size();i++) {
							 v.add(eval((ArrayList)instruction.get(i)));
						 }
						
						 try {	 
								return ((Method) instruction.get(1)).invoke(null,v,env);
						 }
						 catch(InvocationTargetException e){
							 console.err("erreur dans la fonction "+((Method) instruction.get(1)).getName()); 
							 Throwable error = e.getCause();
							 
							 if(error instanceof ExceptionArg || error instanceof ExceptionType) 
								 console.err(error.getMessage());
						 }
						  catch (IllegalArgumentException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
						  } 
						  catch (IllegalAccessException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
						  }
			
						 
				case 3 : ArrayList<Object> v2 = new ArrayList<Object>();
				
						for(int i=2;i<instruction.size();i++) {
								v2.add(eval((ArrayList)instruction.get(i)));
						}
						HashMap<String, Object> penv = makeEnv(Bibliotheque.argFctDefinie.get(instruction.get(1)), v2,Bibliotheque.argOpFctDefinie.get(instruction.get(1)));
						

						try {
						return run(Bibliotheque.corpsFctDefinie.get(instruction.get(1)),penv);
					} catch (ParseException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
						
						
				case 4 : Bibliotheque.argFctDefinie.put((String)instruction.get(1),(ArrayList)instruction.get(2));
						 Bibliotheque.corpsFctDefinie.put((String)instruction.get(1),(ArrayList)instruction.get(4));
						 Bibliotheque.argOpFctDefinie.put((String)instruction.get(1),(ArrayList)instruction.get(3));
						 Bibliotheque.stringFctDefinie.put((String)instruction.get(1),(String)instruction.get(5));
						 return true;
						
			}
			System.out.print("  bug  ");
			return null;
	}
	
}

